package com.xc.smart.interceptor;


import com.xc.smart.annotation.AccessLimit;
import com.xc.smart.exception.RRException;
import com.xc.smart.utlis.R;
import com.xc.smart.utlis.RequestUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
import java.util.concurrent.TimeUnit;

/**  API请求限流拦截器
 * @author mtl
 * 2021/4/25
 */
@Slf4j
@Component
public class AccessLimitInterceptor implements HandlerInterceptor {


    public static final int LIMIT_IP=2;

    @Resource
    private RedisTemplate<String, Integer> redisTemplate;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        try{
            if(handler instanceof HandlerMethod){
                HandlerMethod handlerMethod = (HandlerMethod) handler;
                Method method = handlerMethod.getMethod();
                if(!method.isAnnotationPresent(AccessLimit.class)){
                    return true;
                }
                AccessLimit accessLimit = method.getAnnotation(AccessLimit.class);
                if(accessLimit == null){
                    return true;
                }
                //请求次数
                int times = accessLimit.times();
                //请求时间范围
                int second = accessLimit.second();
                //标识
                int type = accessLimit.type();
                String key = "AccessKey";
                if(type==LIMIT_IP){
                    //通过IP限流
                    key = request.getRemoteAddr();
                }
                //根据key获取已请求次数
                Integer maxTimes = redisTemplate.opsForValue().get(key);
                if(maxTimes == null){
                    //set时一定要加过期时间
                    redisTemplate.opsForValue().set(key, 1, second, TimeUnit.SECONDS);
                }else if(maxTimes < times){
                    redisTemplate.opsForValue().set(key, maxTimes+1, second, TimeUnit.SECONDS);
                }else{
                    //触发限流 服务器繁忙请稍后咨询
                    RequestUtils.out(response, R.error("服务器繁忙请稍后"));
                    return false;
                }
            }
        }catch (Exception e){
            throw new RRException("API请求限流拦截异常,请检查Redis是否开启");
        }
        return true;
    }

}


